# input_fields.py

import functools
import itertools

from Tkinter import *
import tkFileDialog
import tkSimpleDialog
import tkMessageBox
import ttk

from ot_simulator.tk_gui.toplevel import toplevel

class base_field(object):
    def __init__(self, label, attr_accessor=None):
        self.label = label
        self.attr_accessor = attr_accessor

    def render(self, obj, frame, row, col_offset):
        ttk.Label(frame, text=self.label) \
          .grid(row=row, column=col_offset)
        return self.render_entry(obj, frame, row, col_offset + 1)

    def update(self, obj, x):
        value = self.get_value(x)
        if self.attr_accessor is not None:
            self.attr_accessor.set(obj, value)
        #print "input_field", self.label, "returning", value
        return value

class text_field(base_field):
    def render_entry(self, obj, frame, row, col_offset):
        var = StringVar()
        if obj is not None:
            var.set(self.attr_accessor.get(obj) or '')
        ttk.Entry(frame, textvariable=var) \
          .grid(row=row, column=col_offset, sticky=W+E)
        return functools.partial(self.update, obj, var), True, False

    def get_value(self, var):
        return var.get()

class list_field(base_field):
    def __init__(self, label, attr_accessor, list_manager_factory):
        base_field.__init__(self, label, attr_accessor)
        self.list_manager_factory = list_manager_factory

    def render_entry(self, obj, frame, row, col_offset):
        #print "list_field.render_entry:", "frame", frame
        frame.rowconfigure(row, weight=1)
        input_frame = ttk.Frame(frame)
        input_frame.grid(row=row, column=col_offset, sticky=N+S+W+E)
        input_frame.rowconfigure(0, weight=1)
        input_frame.columnconfigure(0, weight = 1)
        if obj is None:
            height = 5
        else:
            height = max(5, len(self.attr_accessor.get(obj) or ()))
        lb = Listbox(input_frame, height=height, selectmode=SINGLE)
        lb.grid(row=0, column=0, sticky=N+S+W+E)
        if obj is None:
            l = None
        else:
            l = self.attr_accessor.get(obj)
            if l:
                for x in l:
                    lb.insert(END, x.name)
        button_frame = ttk.Frame(input_frame)
        button_frame.grid(row=0, column=1, sticky=N+W+E)
        lm = self.list_manager_factory(lb, l, button_frame, 0, 0)
        return functools.partial(self.update, obj, lm), True, True

    def get_value(self, lm):
        return lm.l[:]

class str_list_manager(object):
    def __init__(self, listbox, l, frame, row_offset, col_offset):
        self.listbox = listbox
        if l is None:
            self.l = []
        else:
            self.l = l[:]
        row_offset = self.add_top_buttons(frame, row_offset, col_offset)
        ttk.Button(frame, text="Move up", command=self.move_up) \
          .grid(row=row_offset + 0, column=col_offset)
        ttk.Button(frame, text="Move down", command=self.move_down) \
          .grid(row=row_offset + 1, column=col_offset)
        ttk.Button(frame, text="Delete", command=self.delete) \
          .grid(row=row_offset + 2, column=col_offset)

    def add_top_buttons(self, frame, row_offset, col_offset):
        ttk.Button(frame, text="Add line", command=self.add) \
          .grid(row=row_offset + 0, column=col_offset)
        ttk.Button(frame, text="Edit line", command=self.edit) \
          .grid(row=row_offset + 1, column=col_offset)
        return row_offset + 2

    def move_up(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            if i > 0:
                self.l[i], self.l[i - 1] = self.l[i - 1], self.l[i]
                self.redraw()

    def move_down(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            if i < len(self.l) - 1:
                self.l[i], self.l[i + 1] = self.l[i + 1], self.l[i]
                self.redraw()

    def delete(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            del self.l[i]
            self.redraw()

    def edit(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            new_text = tkSimpleDialog.askstring("Edit text", "new text")
            self.l[i] = new_text
            self.redraw()

    def redraw(self):
        self.listbox.delete(0, END)
        for x in self.l:
            self.listbox.insert(END, self.to_str(x))

    def to_str(self, x):
        return x

class obj_list_manager(str_list_manager):
    @classmethod
    def using(cls, element_class):
        return functools.partial(cls, element_class)

    def __init__(self, element_class, listbox, l,
                 frame, row_offset, col_offset):
        self.element_class = element_class
        str_list_manager.__init__(self, listbox, l, frame, row_offset,
                                  col_offset)

    def add_top_buttons(self, frame, row_offset, col_offset):
        if isinstance(self.element_class, (tuple, list)): 
            self.combobox = ttk.ComboBox(frame, select="readonly",
                                         values=[c.__name__
                                                 for c in self.element_class])
            self.combobox.grid(row=row_offset, column=col_offset)
            row_offset += 1
            ttk.Button(frame, text="Add", command=self.add_combo) \
              .grid(row=row_offset + 0, column=col_offset)
        else:
            ttk.Button(frame, text="Add " + self.element_class.__name__,
                       command=self.add) \
              .grid(row=row_offset + 0, column=col_offset)
        ttk.Button(frame, text="Edit", command=self.edit) \
          .grid(row=row_offset + 1, column=col_offset)
        ttk.Button(frame, text="Rename", command=self.rename) \
          .grid(row=row_offset + 2, column=col_offset)
        return row_offset + 3

    def to_str(self, x):
        return x.name

    def edit(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            toplevel.obj_editor(self.l[i])

    def rename(self):
        sel_list = self.listbox.curselection()
        if sel_list:
            i = int(sel_list[0])
            new_name = tkSimpleDialog.askstring("Rename " + self.l[i].name,
                                                "new name")
            if not self.legal_name(new_name):
                tkMessageBox.showerror(title="Rename " + self.l[i].name,
                                       message="duplicate name: " + new_name)
            else:
                self.l[i].name = new_name
                self.redraw()

    def legal_name(self, name):
        return not any(name == self.to_str(x) for x in self.l)

    def gen_name(self, prefix):
        for i in itertools.count(1):
            name = prefix + '_' + str(i)
            if self.legal_name(name):
                return name

    def add(self):
        print "add", "self.l", repr(self.l)
        self.l.append(
          self.element_class(self.gen_name(self.element_class.__name__)))
        self.redraw()

    def add_combo(self):
        selected = self.combobox.get()
        print "add_combo:", "selected", selected
        element_class = self.element_class[selected]
        self.l.append(element_class(self.gen_name(element_class.__name__)))
        self.redraw()

class output_filename(base_field):
    def __init__(self, label, attr_accessor=None, filetypes=None):
        base_field.__init__(self, label, attr_accessor)
        self.filetypes = filetypes

    def render_entry(self, obj, frame, row, col_offset):
        input_frame = ttk.Frame(frame)
        input_frame.grid(row=row, column=col_offset, sticky=W+E)
        input_frame.columnconfigure(0, weight=1)
        entry = ttk.Entry(input_frame)
        entry.grid(row=0, column=0, sticky=W+E)
        ttk.Button(input_frame, text="Browse...",
                   command=functools.partial(self.set_entry, frame, entry)) \
          .grid(row=0, column=1)
        return functools.partial(self.update, obj, entry), True, False

    def set_entry(self, frame, entry):
        if self.filetypes is None:
            name = tkFileDialog.asksaveasfilename(title=self.label,
                                                  parent=frame)
        else:
            name = tkFileDialog.asksaveasfilename(title=self.label,
                                                  parent=frame,
                                                  filetypes=self.filetypes)
        if name:
            entry.delete(0, END)
            entry.insert(END, name)
            entry.select_range(0, END)

    def get_value(self, entry):
        return entry.get()

